/**************************************************************************************************
* Title: SumPoolAllocator.h
* Author: Gael Huber
* Description: Defines a pool allocator, setting up a dynamically allocated space for memory chunks
*	of a certain size.
**************************************************************************************************/
#ifndef __SUMPOOLALLOCATOR_H__
#define __SUMPOOLALLOCATOR_H__

#include <malloc.h>
#include <assert.h>
#include "SumDataTypes.h"

class PoolAllocator {
public:
	// Constructs a pool allocator with x memory chunks of a certain size.
	inline explicit PoolAllocator(size_t blockSize, unsigned int numBlocks)
		: blockSize(blockSize), totalSize(blockSize * numBlocks), start(0), end(0)
	{
		// Initialize the pool allocator
		initialize(numBlocks);
	}

	// Allocate a block of memory, assuming there are blocks left
	inline void* const alloc(void)
	{
		// Make sure there is space left
		if(start == 0)
			int a = 0;
	//	assert(start != 0);

		// Save the current address
		MemAddress returnValue = (MemAddress) start;

		// Update the start address so that it now points to the address specified by its data
		start = (MemAddress*) ((char*)(*start));

		// Update the end to make sure that it has not been allocated
		if(start == 0)
			end = 0;

		// Return the previous start value
		return (void*) returnValue;
	}

	// Free a block of memory and add it back to the list of remaining blocks
	inline void free(void* mem)
	{
		// If our end no longer exists, point it to the current memory block
		// If start is pointing to nothing, make sure it now points to something again
		if(start == 0) {
			start = end = (MemAddress*) mem;
		}

		// Make sure the current end points to the newly freed address
		*end = (MemAddress) mem;

		// Update the end address so that it now points to the address being freed
		end = (MemAddress*) mem;

		// Ensure that the data in end now points to 0 (i.e. the list ends)
		*end = 0;
	}

	// Peek at the address of the first free block
	inline const MemAddress peek(void) const
	{
		return (MemAddress) start;
	}

	// Return the total size of allocated space
	inline const size_t getTotalSize(void) const
	{
		return totalSize;
	}

	// Return the block size
	inline const size_t getBlockSize(void) const
	{
		return blockSize;
	}

private:
	//void initialize(unsigned int numBlocks);	// Initialize the pool allocator according to specifications.
	void initialize(unsigned int numBlocks) {
		// First, allocate the total number of space
		start = (MemAddress*) malloc(blockSize * numBlocks);
		*start = (MemAddress) start;
		MemAddress* currentAddress = start;
		MemAddress currentValue = *start;

		// Second, iterate through numBlocks and create a link for each block
		for(unsigned int i = 0; i < numBlocks; i++) {
			*currentAddress = currentValue + blockSize;
			currentAddress = (MemAddress*) ((char*)currentAddress + blockSize);
			currentValue += blockSize;
		}

		// Define a starting value for end
		end = (MemAddress*) ((char*)currentAddress - blockSize);
		*end = 0;
	}
	
	size_t blockSize;		// Size of each chunk of memory within the pool
	size_t totalSize;		// Total amount of space contained within this pool
	MemAddress* start;		// Starting address for the linked list of free blocks
	MemAddress* end;		// Ending address for the linked list of free blocks
};

// Overload new and delete for the benefit of the pool allocator

// Overload new for syntactic ease-of-use (permits regular initialization/allocation of objects)
static inline void* operator new (size_t size, PoolAllocator& pa)
{
	return pa.alloc();
}

// Overload new[] for syntactic ease-of-use (permits regular initialization/allocation of arrays)
static inline void* operator new[] (size_t size, PoolAllocator& pa)
{
	return pa.alloc();
}

// Overload delete for syntactic ease-of-use (permits regular deletion of objects)
static inline void operator delete (void* mem, PoolAllocator& pa)
{
	pa.free(mem);
}

// Overload delete[] for syntactic ease-of-use (permits regular deletion of array objects
static inline void operator delete[] (void* mem, PoolAllocator& pa)
{
	pa.free(mem);
}

#endif